package se.thoka.twt.client.application;

import java.util.ArrayList;
import java.util.List;

import se.thoka.twt.client.users.AbstractUser;
import se.thoka.twt.client.users.UserClientDatastoreManager;
import se.thoka.twt.client.users.UserLoggedInEvent;
import se.thoka.twt.client.users.UserLoggedInHandler;
import se.thoka.twt.client.users.UserLoggedOutEvent;
import se.thoka.twt.client.users.UserLoggedOutHandler;
import se.thoka.twt.client.utils.StringUtils;
import se.thoka.twt.client.utils.TWTLogLevel;
import se.thoka.twt.client.utils.TWTLogger;
import se.thoka.twt.shared.exceptions.UserLoggedInRequiredException;

import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.HasHandlers;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.user.client.History;

public abstract class TWTViewController<U extends AbstractUser> implements ValueChangeHandler<String>, UserLoggedInHandler<U>, UserLoggedOutHandler, HasHandlers {
	
	private static TWTViewController<?> instance;
	
	private UserClientDatastoreManager<?, ?> userManager;
	private List<HistoryComponent> historyComponents;
	private String defaultLoggedOutToken, defaultLoggedInToken, currentToken;
	protected final SimpleEventBus eventBus;
	
	public static TWTViewController<?> getInstance() {
		return instance;
	}
	
	protected TWTViewController(UserClientDatastoreManager<U, ?> userManager) {
		if (instance != null) {
			throw new IllegalStateException("Only one instance of view controller is allowed.");
		}
		instance = this;
		TWTLogger.log(TWTLogLevel.DEBUG, "TWTViewController instanced to " + instance.getClass().getName());
		this.userManager = userManager;
		this.historyComponents = new ArrayList<HistoryComponent>();
		this.eventBus = new SimpleEventBus();
		
		initiateHistoryComponents();
		
		History.addValueChangeHandler(this);

		if (userManager != null) {
			userManager.addUserLoggedInHandler(this);
			userManager.addUserLoggedOutHandler(this);
		}
		newHistory(getCurrentToken());
	}
	
	public HandlerRegistration addHistoryChangedHandler(HistoryChangedHandler handler) {
		return eventBus.addHandler(HistoryChangedEvent.getType(), handler);
	}
	
	@Override
	public void onValueChange(ValueChangeEvent<String> event) {
		onHistoryChanged(event.getValue());
	}
	
	@Override
	public void onUserLoggedIn(UserLoggedInEvent<U> event) {
		goToDefault();
	}
	
	@Override
	public void onUserLoggedOut(UserLoggedOutEvent event) {
		goToDefault();
	}
	
	@Override
	public void fireEvent(GwtEvent<?> event) {
		eventBus.fireEvent(event);
	}
	
	public static String getCurrentToken() {
		return History.getToken();
	}
	
	public static String getDefaultToken() {
		return instance.defaultToken();
	}
	
	public static void goToDefault() {
		newHistory(getDefaultToken());
	}
	
	public static void newHistory(String token) {
		if (!StringUtils.equal(getCurrentToken(), token)) {
			History.newItem(token);
		} else {
			// Allow refresh
			getInstance().onHistoryChanged(token);
		}
	}
	
	protected String defaultToken() {
		return isLoggedIn() ? defaultLoggedInToken : defaultLoggedOutToken;
	}
	
	protected abstract void initiateHistoryComponents();
	
	protected void addHistoryComponent(HistoryComponent component) {
		historyComponents.add(component);
		
		if (defaultLoggedOutToken == null && component.getActivation().activate(false)) {
			defaultLoggedOutToken = component.getDefaultToken();
		}
		if (defaultLoggedInToken == null && component.getActivation().activate(true)) {
			defaultLoggedInToken = component.getDefaultToken();
		}
	}
	
	protected void onHistoryChanged(String token) {
		String oldToken = currentToken;
		currentToken = token;

		boolean userSet = isLoggedIn();
		TWTLogger.log(TWTLogLevel.DEBUG, "#onHistoryChanged(" + token + ") UserSet? " + userSet);
		List<HistoryComponent> activateComponents = new ArrayList<HistoryComponent>();
		for (HistoryComponent component : historyComponents) {
			if (component.activate(token, userSet)) {
				activateComponents.add(component);
			} else {
				component.hide();
			}
		}
		
		if (!activateComponents.isEmpty()) {
			for (HistoryComponent component : activateComponents) {
				try {
					TWTLogger.log(TWTLogLevel.DEBUG, " - Showing component: " + component.getClass().getName());
					component.show();
				} catch (UserLoggedInRequiredException e) {
					TWTLogger.log(TWTLogLevel.DEBUG, " - Refreshing component: " + component.getClass().getName());
					onLoginRequired();
					return;
				}
			}
		} else {
			onHistoryNotFound(token);
			return;
		}
		
		fireEvent(new HistoryChangedEvent(token, oldToken));
	}
	
	protected void onHistoryNotFound(String token) {
		TWTLogger.log(TWTLogLevel.WARNING, "#onHistoryNotFound(" + token + ")");
		History.newItem(defaultToken());
	}
	
	protected void onLoginRequired() {
		History.newItem(defaultToken());
	}
	
	protected boolean isLoggedIn() {
		return userManager != null && userManager.isUserLoggedIn();
	}
}
